package student

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

type SchoolInformation struct {
	SchoolName string
	Address    string
	SchoolType string
}

// Student 结构体代表了一个学生的基本信息。
// 它包括学生的ID、姓名、年龄、性别和年级。
type Student struct {
	ID    int    // ID 字段用于唯一标识一个学生。
	Name  string // Name 字段记录了学生的姓名。
	Age   int    // Age 字段表示学生的年龄。
	Sex   string // Sex 字段描述了学生的性别。
	Grade int    // Grade 字段反映了学生所在的年级。
	SchoolInformation
}

func (schoolInfor SchoolInformation) SchoolInformationPrinter() {
	fmt.Printf("学校名称：%s\n", schoolInfor.SchoolName)
	fmt.Printf("学校地址：%s\n", schoolInfor.Address)
	fmt.Printf("学校类型：%s\n", schoolInfor.SchoolType)
	fmt.Println("----------------------------")
}

// StudentsPrinter 打印学生信息
// 该方法属于 Student 类型，用于以格式化的方式输出学生的信息，包括姓名、年龄、性别和年级。
// 没有返回值。
func (student Student) StudentsPrinter() {
	// 打印分隔线，用于区分不同学生的信息
	fmt.Println("----------------------------")
	// 格式化打印学生的姓名
	fmt.Printf("学生姓名：%s\n", student.Name)
	// 格式化打印学生的年龄
	fmt.Printf("学生年龄：%d\n", student.Age)
	// 格式化打印学生的性别
	fmt.Printf("学生性别：%s\n", student.Sex)
	// 格式化打印学生的年级
	fmt.Printf("学生年级：%d\n", student.Grade)
	// 调用 SchoolInformationPrinter 方法，打印学生的学校信息
	student.SchoolInformation.SchoolInformationPrinter()
	// 再次打印分隔线，结束当前学生信息的输出
	fmt.Println("----------------------------")
}

// IfAdult 判断学生是否成年
// 参数:
//
//	adult: 成年的最低年龄标准
//
// 该方法通过比较学生的年龄和成年标准来判断学生是否成年
func (student Student) IfAdult(adult int) {
	// 判断学生的年龄是否大于等于18岁
	if student.Age >= adult {
		// 如果大于等于18岁，则打印提示信息
		fmt.Println("该学生已经成年")
	} else {
		// 如果小于18岁，则打印提示信息
		fmt.Println("该学生还未成年")
	}
}

func GetStudentInformation() {
	var schoolInformation [3]SchoolInformation
	schoolInformation[0] = SchoolInformation{SchoolName: "第一中学", Address: "北京", SchoolType: "普通中学"}
	schoolInformation[1] = SchoolInformation{SchoolName: "第二中学", Address: "上海", SchoolType: "普通中学"}
	schoolInformation[2] = SchoolInformation{SchoolName: "第三中学", Address: "广州", SchoolType: "普通中学"}

	var students [3]Student
	students[0] = Student{ID: 1, Name: "张三", Age: 18, Sex: "男", Grade: 3, SchoolInformation: schoolInformation[0]}
	students[1] = Student{ID: 2, Name: "李四", Age: 17, Sex: "女", Grade: 3, SchoolInformation: schoolInformation[1]}
	students[2] = Student{ID: 3, Name: "王五", Age: 16, Sex: "男", Grade: 3, SchoolInformation: schoolInformation[2]}
	for _, student := range students {
		student.StudentsPrinter()
		student.IfAdult(18)
		//fmt.Printf("学生ID：%d，姓名：%s，年龄：%d，性别：%s，年级：%d\n", student.ID, student.Name, student.Age, student.Sex, student.Grade)
	}
}

// InsertStudentInformation 用于输入并返回3个学生的信息数组
// 该函数无需参数
// 返回值是一个包含3个学生信息的数组，每个学生的信息包括ID、姓名、年龄、性别和年级
func InsertStudentInformation() [3]Student {
	// 初始化一个可容纳3个学生信息的数组
	var students [3]Student

	// 循环输入3个学生的信息
	for i := 0; i < 3; i++ {
		// 提示用户输入第i+1个学生的信息
		fmt.Printf("请输入第%d个学生的信息：\n", i+1)

		// 分别提示并读取学生的ID、姓名、年龄、性别和年级信息
		fmt.Print("ID：")
		fmt.Scanf("%d", &students[i].ID)
		fmt.Println()
		fmt.Print("姓名：")
		fmt.Scanf("%s", &students[i].Name)
		fmt.Println()
		fmt.Print("年龄：")
		fmt.Scanf("%d", &students[i].Age)
		fmt.Println()
		fmt.Print("性别：")
		fmt.Scanf("%s", &students[i].Sex)
		fmt.Println()
		fmt.Print("年级：")
		fmt.Scanf("%d", &students[i].Grade)

		// 新增一个学生信息后换行，以提高控制台输出的可读性
		fmt.Println()
	}

	// 返回收集到的学生信息数组
	return students
}

// InsertStudentInformationIntoSlice 用于输入并返回多个学生的信息切片
// 该函数无需参数
// 返回值是一个包含学生信息的切片，每个学生的信息包括ID、姓名、年龄、性别和年级
func InsertStudentInformationIntoSlice() []Student {
	// 提示用户输入学生数量
	var count int
	fmt.Print("请输入学生数量：")
	fmt.Scanln(&count)

	// 初始化一个可容纳count个学生信息的切片
	students := make([]Student, 0, count)

	// 循环输入学生的信息
	for i := 0; i < count; i++ {
		// 提示用户输入第i+1个学生的信息
		fmt.Printf("请输入第%d个学生的信息：\n", i+1)

		// 定义一个临时变量存储单个学生的信息
		var student Student

		// 分别提示并读取学生的ID、姓名、年龄、性别和年级信息
		fmt.Print("ID：")
		fmt.Scanln(&student.ID)

		//fmt.Print("姓名：")
		//fmt.Scanln(&student.Name)
		// 年龄校验循环
		for {
			var ageStr string
			fmt.Print("年龄（0-130之间的数字）：")
			fmt.Scanln(&ageStr)

			// 正则匹配是否是纯数字
			matched, err := regexp.MatchString(`^\d+$`, ageStr)
			if err != nil || !matched {
				fmt.Println("输入无效，请输入一个正整数！")
				continue
			}

			age, _ := strconv.Atoi(ageStr)
			if age < 0 || age > 130 {
				fmt.Println("年龄必须在 0 到 130 之间，请重新输入！")
				continue
			}

			student.Age = age
			break
		}

		fmt.Print("年龄：")
		fmt.Scanln(&student.Age)

		fmt.Print("性别：")
		fmt.Scanln(&student.Sex)

		fmt.Print("年级：")
		fmt.Scanln(&student.Grade)

		// 将当前学生添加到切片中
		students = append(students, student)

		// 新增一个学生信息后换行，以提高控制台输出的可读性
		fmt.Println()
	}

	// 返回收集到的学生信息切片
	return students
}

// InsertStudentInformationMap 用于输入学生信息并存储到传入的 map 中
// 参数:
//
//	students: 一个 map，key 是字符串（如 "ID_1"），value 是 Student 结构体
//	该函数没有返回值，直接修改传入的 map
func InsertStudentInformationMap(students map[string]Student) {
	for {
		var choice string
		fmt.Print("是否继续录入学生信息？(y/n): ")
		fmt.Scanln(&choice)

		if choice != "y" && choice != "Y" {
			break
		}

		var student Student

		fmt.Print("ID：")
		fmt.Scanln(&student.ID)

		fmt.Print("姓名：")
		fmt.Scanln(&student.Name)

		fmt.Print("年龄：")
		fmt.Scanln(&student.Age)

		fmt.Print("性别：")
		fmt.Scanln(&student.Sex)

		fmt.Print("年级：")
		fmt.Scanln(&student.Grade)

		fmt.Print("学校名称：")
		fmt.Scanln(&student.SchoolName)

		fmt.Print("学校地址：")
		fmt.Scanln(&student.Address)

		fmt.Print("学校类型：")
		fmt.Scanln(&student.SchoolType)

		key := fmt.Sprintf("ID_%d", student.ID)
		students[key] = student

		fmt.Println("学生信息已保存。")
		fmt.Println()
	}
}

// FindStudentByName 根据姓名查找学生并显示详细信息
func FindStudentByName(students []Student, searchName string) {
	found := false
	for _, student := range students {
		// 使用 strings.Contains 判断学生姓名中是否包含目标姓名（支持模糊匹配）
		if strings.Contains(strings.ToLower(student.Name), strings.ToLower(searchName)) {
			student.StudentsPrinter() // 调用 StudentsPrinter 方法打印学生详细信息
			found = true
		}
	}

	if !found {
		fmt.Println("查无此人")
	}
}
